--display:create ( 'Cellular Automaton', 800, 600, 32, true )


local dm = DisplayMode ( )
dm.width = 800
dm.height = 600
dm.depth = 32
dm.windowed = true
display:set_mode ( dm )


local w = 200
local h = 150

local map = {}
for y = 1, h do
	table.insert ( map, {} )
	for x = 1, w do
		map[y][x] = 0
	end
end

function reset_row ( y )
	for x = 1, w do
		map[1][x] = 0
	end
	local c = math.floor ( w / 2 )
	map[1][c] = 1
end

reset_row ( 1 )


-- 111 110 101 100 011 010 001 000

rules = {}

function tobinary ( decimal, minsize )
	local binary = {}
	local n = decimal
	local i = 1
	while n > 0 do
		table.insert ( binary, 1, n % 2 )

		n = math.floor ( n / 2 )
	end
	if minsize then
		while #binary < minsize do
			table.insert ( binary, 1, 0 )
		end	
	end
	return binary
end

function todecimal ( binary )
	local decimal = 0

	for i = #binary, 1, -1 do
		local d = binary[i]
		local pow = ( #binary - i )
		decimal = decimal + d * ( 2 ^ pow )
	end
	return decimal
end



function update_map ( rules, sy )
	sy = sy or 2
	for y = sy, h do
		for x = 1, w do
			local x2 = x - 1
			if x2 == 0 then
				x2 = w
			end
			local p1 = map[y - 1][x2]
			local p2 = map[y - 1][x]
			local x3 = x + 1
			if x3 > #map[1] then
				x3 = 1
			end
			local p3 = map[y - 1][x3]

			local bin = { p1, p2, p3 }
			local d = todecimal ( bin ) + 1

			map[y][x] = rules[d]
		end
	end
end

local rule = 128

local t = 4

local sprite = Sprite ( )
sprite.depth = -1
display.viewport:add_child ( sprite )

local font = Font ( )
font:load_system ( "Courier New", 12 )

local c = sprite.canvas


local rect = Canvas ( )
rect:rectangle ( t, t )
rect:fill ( )

sprites = {}
for y = 1, h do
	sprites[y] = {}
	for x = 1, w do
		local ox = -w * t / 2
		local oy = h * t / 2
		local s = Sprite ( ox + x * t, oy + -y * t )
		s.canvas = rect
		sprites[y][x] = s
		display.viewport:add_child ( s )
	end
end

function redraw_map ( )
	for y = 1, h do
		for x = 1, w do
			local s = sprites[y][x]
			if map[y][x] == 1 then
				s.visible = true
			else
				s.visible = false
			end
		end
	end
end

keyboard.on_press = function ( keyboard, key )
	if key == KEY_LEFT then
		rule = math.min ( rule + 1, 255 )
		reset_row ( 1 )
	elseif key == KEY_RIGHT then
		rule = math.max ( rule - 1, 0 )
		reset_row ( 1 )
	elseif key == KEY_F1 then
		help = not help
	else
		return
	end

	local rules = tobinary ( rule, 8 )
	update_map ( rules )
	redraw_map ( )
end

keyboard:on_press ( KEY_LEFT )

help = true

timer = Timer ( )
timer.on_tick = function ( timer )
	c:clear ( )
	if help == true then

	local lh = font:get_height ( )
	c:set_font ( font, RED )
	c:move_to ( -w * t / 2, h * t / 2 )
	c:rel_move_to ( 10, -23 )
	c:write ( "1-Dimensional cellular automation demo" )
	c:rel_move_to ( 0, -lh )
	c:write ( "Rule - " .. 255 - rule )
	c:rel_move_to ( 0, -lh )
	c:write ( "KEY_LEFT - previous rule" )
	c:rel_move_to ( 0, -lh )
	c:write ( "KEY_RIGHT - next rule" )
	c:rel_move_to ( 0, -lh )
	c:write ( "KEY_DOWN - advance" )
	c:rel_move_to ( 0, -lh )
	c:write ( "KEY_F1 - toggle help" )

	end

	if keyboard:is_down ( KEY_DOWN ) == true then
		local ss = 1
		for i = 1, ss do
			local row = table.remove ( map, 1 )
			table.insert ( map, row )
		end
		local rules = tobinary ( rule, 8 )
		update_map ( rules, h - ss )
		redraw_map ( )
	end
end
timer:start ( 16, true )